Skip to content

[example] Add example for Swift Service Lifecycle #522

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 46 commits into
base: main
Choose a base branch
from

Conversation

sebsto
Copy link
Contributor

@sebsto sebsto commented Jun 30, 2025

Now that task cancellation works, re publishing this PR with a new example for Swift Service Lifecycle

@sebsto sebsto self-assigned this Jun 30, 2025
@sebsto sebsto added the 🔨 semver/patch No public API change. label Jun 30, 2025
@sebsto sebsto changed the title [example] Add example for Swift Service Lifecycle [WIP] DO NOT MERGER - [example] Add example for Swift Service Lifecycle Jun 30, 2025
@sebsto sebsto changed the title [WIP] DO NOT MERGER - [example] Add example for Swift Service Lifecycle [WIP] DO NOT MERGE - [example] Add example for Swift Service Lifecycle Jun 30, 2025
@sebsto sebsto marked this pull request as draft July 15, 2025 10:51
@sebsto sebsto force-pushed the sebsto/servicelifecycle branch from 2432ed3 to 018d9ce Compare July 23, 2025 15:52
@sebsto sebsto changed the title [WIP] DO NOT MERGE - [example] Add example for Swift Service Lifecycle [example] Add example for Swift Service Lifecycle Jul 23, 2025
@sebsto sebsto marked this pull request as ready for review July 23, 2025 15:53
@sebsto sebsto requested review from adam-fowler and Copilot July 25, 2025 17:49
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds a comprehensive example demonstrating Swift Service Lifecycle integration with AWS Lambda and PostgreSQL RDS, along with minor fixes to Docker command references and local server implementation.

  • Adds a complete ServiceLifecycle+Postgres example with AWS infrastructure setup
  • Fixes Docker command references in format checking scripts
  • Updates local server implementation to handle return values properly

Reviewed Changes

Copilot reviewed 18 out of 18 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
scripts/check-format-linux.sh Fixed Docker command references from 'container' to 'docker'
Sources/AWSLambdaRuntime/Lambda+LocalServer.swift Updated return type handling and error management in local server
Examples/Streaming/samconfig.toml Added SAM configuration for streaming example
Examples/ServiceLifecycle+Postgres/* Complete new example with PostgreSQL integration, infrastructure templates, and documentation
.licenseignore Added .toml files to license ignore list
.github/workflows/pull_request.yml Added new example to CI workflow

@@ -106,7 +106,7 @@ internal struct LambdaHTTPServer {
eventLoopGroup: MultiThreadedEventLoopGroup = .singleton,
logger: Logger,
_ closure: sending @escaping () async throws -> Result
) async throws -> Result {
) async throws -> Swift.Result<Result, any Error> {
Copy link
Preview

Copilot AI Jul 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The return type change from Result to Swift.Result<Result, any Error> appears to be a breaking change that could affect API consumers. This should be carefully considered for backward compatibility.

Suggested change
) async throws -> Swift.Result<Result, any Error> {
) async throws -> Result {

Copilot uses AI. Check for mistakes.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So when does this function throw an error and when does it return the error as a Result?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of interest what was causing the shutdown error, that you are ignoring now

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So when does this function throw an error and when does it return the error as a Result?

when the user types CTRL-C to stop the server and the server is part of a service lifecycle group

Out of interest what was causing the shutdown error, that you are ignoring now

The reported error is Channel I/O by an attempt to read/write on a closed channel.
I don't know the root cause.

Because this happens on the local server, just used for testing, and at the time of shutdown. I think it's sfe to ignore the error and let the app terminate.

@sebsto
Copy link
Contributor Author

sebsto commented Jul 26, 2025

@adam-fowler This is a new example.
I made a tiny change to the local HTTP server to avoid a crash + backtrace on CTRL-C, let me know if you think there is a more elegant way to solve that. (the root cause is a Channel I/O error on CTRL-C, looks like we still attempt to send data on a closed channel)

The rest is "just" a PostgreSQL + service lifecycle example. I provide a SAM template to setup the database in a secure way (private network, restrictive security groups, password stored in secret manager)

// TODO: ideally, I want to do this once, after serviceGroup.run() is done
// but before the handler is called
logger.trace("Checking database")
try await prepareDatabase()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For Hummingbird I created a wrapper service for a service which called a closure before running the wrapped service. This allowed me to have prelude code like your prepareDatabase. You could use this to wrap the lambda runtime and run the prepareDatabase beforehand

/// Wrap another service to run after a prelude closure has completed
struct PreludeService<S: Service>: Service, CustomStringConvertible {
    let prelude: @Sendable () async throws -> Void
    let service: S

    var description: String {
        "PreludeService<\(S.self)>"
    }

    init(service: S, prelude: @escaping @Sendable () async throws -> Void) {
        self.service = service
        self.prelude = prelude
    }

    func run() async throws {
        try await self.prelude()
        try await self.service.run()
    }
}

extension Service {
    /// Build existential ``PreludeService`` from an existential `Service`
    func withPrelude(_ prelude: @escaping @Sendable () async throws -> Void) -> Service {
        PreludeService(service: self, prelude: prelude)
    }
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using the PostgresNIO driver, can we run a prelude code (that will send SQL commands on the connection) before service.run() ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just tried it and the answer is "yes" :-)

@@ -106,7 +106,7 @@ internal struct LambdaHTTPServer {
eventLoopGroup: MultiThreadedEventLoopGroup = .singleton,
logger: Logger,
_ closure: sending @escaping () async throws -> Result
) async throws -> Result {
) async throws -> Swift.Result<Result, any Error> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So when does this function throw an error and when does it return the error as a Result?

@@ -106,7 +106,7 @@ internal struct LambdaHTTPServer {
eventLoopGroup: MultiThreadedEventLoopGroup = .singleton,
logger: Logger,
_ closure: sending @escaping () async throws -> Result
) async throws -> Result {
) async throws -> Swift.Result<Result, any Error> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of interest what was causing the shutdown error, that you are ignoring now

@sebsto
Copy link
Contributor Author

sebsto commented Jul 29, 2025

@adam-fowler Thank you for the initial review. I added PreludeService and answered your questions

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🔨 semver/patch No public API change.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants